#include "GameApp.h"

#include "Utility/Utility.h"
#include "Common/CommonTypes.h"
#include "Renderer/D3D10RenderDevice.h"
#include "GameWorld/GameWorld.h"
#include "Common/Constants.h"
#include "Scene/Engine.h"

static const int DEFAULT_WINDOW_WIDTH = 800;
static const int DEFAULT_WINDOW_HEIGHT = 600;

static GameApp* s_App = NULL;
GameApp* GetApp()
{
	return s_App;
}

LRESULT CALLBACK WinProc( HWND hWnd, UINT32 msg, WPARAM wParam, LPARAM lParam )
{
	static GameApp* pApp = 0;

	switch(msg)
	{
	case WM_CREATE:
		{
			CREATESTRUCT* pCreateStruct = (CREATESTRUCT*)lParam;
			pApp = (GameApp*)pCreateStruct->lpCreateParams;
			return 0;
		}		
	}
	if (pApp)
		return pApp->WinProc(msg, wParam, lParam);
	else
		return DefWindowProc(hWnd, msg, wParam, lParam);
}

//////////////////////////////////////////////////////////////////////////

GameApp::GameApp( HINSTANCE hInstance )
	: m_hInstance(hInstance)
	, m_hMainWnd(0)
	, m_bAppPause(false)
	, m_bMinimized(false)
	, m_bMaximized(false)	
	, m_bResizing(false)
	, m_wstrWinCaption(L"GameApp 1.0")
	, m_pEngine(NULL)
	, m_wstrFrameStats(L"")
	, m_pGameWorld(NULL)
{
	s_App = this;
}

GameApp::~GameApp()
{	
	SAFE_DELETE(m_pGameWorld);
	SAFE_DELETE(m_pEngine);
}

bool GameApp::Init()
{
	_InitMainWindow();
	
	return OnInit();
}

HINSTANCE GameApp::GetAppInstance()
{
	return m_hInstance;
}

HWND GameApp::GetMainWindow()
{
	return m_hMainWnd;
}

int GameApp::Run()
{
	MSG  msg = {0};

	m_oTimer.reset();
	while(msg.message != WM_QUIT)
	{
		if (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
		else
		{
			m_oTimer.tick();
			float fTimeElapsed = m_oTimer.getDeltaTime();
			if (!m_bAppPause)
			{
				OnUpdate(fTimeElapsed);
			}
			else
			{
				::Sleep(50);
			}
			OnRender();
		}
	}
	return (int)msg.wParam;
}

bool GameApp::OnInit()
{
	m_pEngine = new CEngine();
	m_pEngine->Init(m_hMainWnd, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
	SetEngineInstance(m_pEngine);

	m_pGameWorld = new CGameWorld;
	m_pGameWorld->Init();

	return true;
}

void GameApp::OnUpdate( float fTimeElapsed )
{
	_CalculateStats();

	m_pGameWorld->Update(fTimeElapsed);
	m_pEngine->Update(fTimeElapsed);
}

void GameApp::OnRender()
{
	m_pEngine->Render();

	_DisplayStats();	

	m_pEngine->SwapBuffer();
}

void GameApp::_InitMainWindow()
{
	WNDCLASS   wc;
	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = ::WinProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = m_hInstance;
	wc.hIcon         = LoadIcon(0, IDI_APPLICATION);
	wc.hCursor       = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
	wc.lpszMenuName  = 0;
	wc.lpszClassName = L"D3DWndClassName";

	if (!RegisterClass(&wc))
	{
		MessageBox(0, L"Register Class Failed", 0, 0);
		PostQuitMessage(0);
	}

	RECT rect = {0, 0, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT};
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false);
	int nWidth  = rect.right - rect.left;
	int nHeight = rect.bottom - rect.top;

	m_hMainWnd = CreateWindow(L"D3DWndClassName", m_wstrWinCaption.c_str(),
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 
		nWidth, nHeight, 0, 0, m_hInstance, this);

	if (!m_hMainWnd)
	{
		MessageBox(0, L"Create Window Failed", 0, 0);
		PostQuitMessage(0);
	}

	ShowWindow(m_hMainWnd, SW_SHOW);
	UpdateWindow(m_hMainWnd);
}

void GameApp::_CalculateStats()
{
	static int nFrameCount = 0;
	static float fTimeBase = 0.0f;

	nFrameCount++;

	if ((m_oTimer.getGameTime() - fTimeBase) >= 1.0f)
	{
		float fFps = (float)nFrameCount;
		float fMSPerFrame = 1000.0f / fFps;

		std::wostringstream oss;
		oss.precision(6);
		oss<<L"FPS: "<<fFps<<L"\n"
			<<L"Milliseconds Per Frame: "<<fMSPerFrame;

		m_wstrFrameStats = oss.str();

		nFrameCount = 0;
		fTimeBase += 1.0f;
	}
}

void GameApp::_DisplayStats()
{
	static RECT R = {5, 5, 0, 0};
	
	m_pEngine->GetRenderDevice()->ShowText(m_wstrFrameStats, &R, BLACK);
}

LRESULT GameApp::WinProc( UINT32 msg, WPARAM wParam, LPARAM lParam )
{
	int nClientWidth, nClientHeight;
	switch(msg)
	{
	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_INACTIVE)
		{
			m_bAppPause = true;
			m_oTimer.stop();
		} 
		else
		{
			m_bAppPause = false;
			m_oTimer.start();
		}
		return 0;
	case WM_SIZE:
		nClientWidth = LOWORD(lParam);
		nClientHeight = HIWORD(lParam);
		if (m_pEngine)
		{
			if (wParam == SIZE_MINIMIZED)
			{
				m_bAppPause  = true;
				m_bMinimized = true;
				m_bMaximized = false;
			} 
			else if (wParam == SIZE_MAXIMIZED)
			{
				m_bAppPause  = false;
				m_bMinimized = false;
				m_bMaximized = true;
			}
			else if (wParam == SIZE_RESTORED)
			{
				if (m_bMinimized)
				{
					m_bAppPause  = false;
					m_bMinimized = false;
					m_pEngine->OnWindowResize(nClientWidth, nClientHeight);
				} 
				else if (m_bMaximized)
				{
					m_bAppPause  = false;
					m_bMaximized = false;
					m_pEngine->OnWindowResize(nClientWidth, nClientHeight);
				}
				else if (m_bResizing)
				{

				} 
				else
				{
					m_pEngine->OnWindowResize(nClientWidth, nClientHeight);
				}
			}
		}
		return 0;
	case WM_ENTERSIZEMOVE:
		m_bAppPause = true;
		m_bResizing = true;
		m_oTimer.stop();
		return 0;

	case WM_EXITSIZEMOVE:
		m_bAppPause = false;
		m_bResizing = false;
		m_oTimer.start();
		//_OnWindowResize();
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_MENUCHAR:
		return MAKELRESULT(0, MNC_CLOSE);

	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lParam)->ptMinTrackSize.x = 800;
		((MINMAXINFO*)lParam)->ptMinTrackSize.y = 600;
		return 0;
	}

	return DefWindowProc(m_hMainWnd, msg, wParam, lParam);
}
